﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
using System.Xml;
using eMisc;
using System.Collections;
using System.Reflection;
using System.Data;
using eLib.ImportWizard;

namespace eLib
{
    [Guid("D06609FD-8FD4-4bb5-88E1-DB74AE10C17E")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    /// <summary>
    /// базовый класс работы с данными
    /// параметры могут быть указаны либо на каждый год, либо в общем 
    /// некоторые параметры могут быть сгруппированы, например ГТМ (разделитель группа|параметр)
    /// 
    /// </summary>
    public class BaseData
    {
        // структура для хранения данных
        protected Dictionary<int, Dictionary<string, double>> data = new Dictionary<int, Dictionary<string, double>>();
        // параметры <параметр, загружаемый(не расчетный)>
        protected Dictionary<string, bool> _params = new Dictionary<string, bool>();

        private string name;
        private string shortName; //синоним
        private WorkSpace workSpace;
        //конструктор
        public BaseData(WorkSpace WorkSpace,string Name)
        {
            this.workSpace = WorkSpace;
            this.name = Name;
            defaultParams();
        }
        //параметры
        protected virtual void defaultParams()
        {
            _params.Clear();
        }
        //формулы
        public virtual void setCalculations()
        {
        }
        // PostCalculations добавлеят в список функции(делегаты) подобные как в setCalculations()
        // это сделано для того, чтоб "досчитать" зависимые параметры 
        public virtual void setPostCalculations()
        {
        }

        public void AddParam(string paramName, bool Loadable)
        {
            string _k = ""; bool fnd=false;
            foreach (KeyValuePair<string, bool> k in _params)
            {
                if (k.Key.ToLower() == paramName.ToLower())
                {
                    _k = k.Key;
                    fnd = true;
                    break;
                }
            }
            if (fnd) _params[_k] = Loadable;
            else _params.Add(paramName, Loadable);
        }
        public bool ParamExist(string paramName)
        {
            bool res = false;
            foreach (KeyValuePair<string, bool> k in _params)
                if (string.Compare(k.Key, paramName, true) == 0) { res = true; break; }
            return res;
        }

        //
        public string Name
        {
            get { return name; }
        }
        public string ShortName
        {
            get
            {
                if (string.IsNullOrEmpty(shortName)) return name;
                else return shortName;
            }
            set
            {
                if (value != "" && value != shortName)
                {
                    //проверяем в WorkSpace на наличие BaseData с таким же синонимом (воибежание недоразумений)
                    bool f = false;
                    if (workSpace != null)
                        foreach (BaseData bd in workSpace.Data)
                            if (bd.Name.ToLower().Trim() == value.ToLower().Trim() ||
                                bd.ShortName.ToLower().Trim() == value.ToLower().Trim())
                            {
                                f = true;
                                break;
                            }
                    if (!f) shortName = value.Trim();
                }
            }
        }
        public WorkSpace WorkSpace
        {
            get { return workSpace; }
        }

        public void Clear()
        {
            data.Clear();
            defaultParams();
        }
        
        public int Count
        {
            get { return data.Count; }
        }

        #region CY
        private const int _cy = 0; //для параметров, у которых не указан год (как [Ны] в старом варианте)
        private bool _cyPresented(string paramName)
        {
            Dictionary<string, double> v;
            bool res = data.TryGetValue(_cy, out v) && v.ContainsKey(paramName.ToLower());
            return res;
        }
        private void _cyRemove(string paramName)
        {
            Dictionary<string, double> v;
            if (data.TryGetValue(_cy, out v) && v.ContainsKey(paramName.ToLower()))
            {
                v.Remove(paramName.ToLower());
                data[_cy] = v;
            }
        }
        private void non_cyRemove(string paramName)
        {
            List<int> lp = new List<int>();
            foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
                if (isYearActual(k.Key) && k.Value.ContainsKey(paramName.ToLower()))
                    lp.Add(k.Key);
            foreach (int i in lp)
                data[i].Remove(paramName.ToLower());
        }
        protected bool isYearActual(int year)
        {
            return year != _cy;
        }
        #endregion

        public void AddYear(int year)
        {
            if (!data.ContainsKey(year))
                data.Add(year, new Dictionary<string, double>());
        }

        public void removeYear(int year)
        {
            if (data.ContainsKey(year))
                data.Remove(year);
        }

        public void Add(string paramName, int year, double value)
        {
            Dictionary<string, double> v;
            bool exists = data.ContainsKey(year);
            if (exists) v = data[year];
            else v = new Dictionary<string, double>();
            if (v.ContainsKey(paramName.ToLower()))
            {
                if (workSpace==null || !workSpace.UnderCalculating)
                {
                    // check year for _cy
                    if (isYearActual(year) && _cyPresented(paramName))
                    {
                        // если имеем данные по годам, то для year = _cy удаляем
                        _cyRemove(paramName);
                    }
                    else
                        if (!isYearActual(year))
                        {
                            // и наоборот
                            non_cyRemove(paramName);
                        }
                }
                v[paramName.ToLower()] = value;
            }
            else v.Add(paramName.ToLower(), value);
            if (exists) data[year] = v;
            else data.Add(year, v);
            //
            if (!ParamExist(paramName)) _params.Add(paramName, false);
        }
        public void Add(string paramName, double value)
        {
            Add(paramName, _cy, value);
        }

        public double Get(string paramName, int year)
        {
            double res = 0;
            Dictionary<string, double> v;
            if (data.TryGetValue(year, out v) && v.ContainsKey(paramName.ToLower()))
                res = v[paramName.ToLower()];
            else
            {
                if (isYearActual(year)) res = Get(paramName, _cy);
            }
            return res;
        }
        //private get
        protected bool _get1(string paramName, int year, out double value)
        {
            bool res = false; value = 0;
            Dictionary<string, double> v;
            if (data.TryGetValue(year, out v) && v.ContainsKey(paramName.ToLower()))
            {
                value = v[paramName.ToLower()];
                res = true;
            }
            else
            {
                if (isYearActual(year)) res = _get1(paramName,_cy,out value);
            }
            return res;
        }

        //вспомогательные методы
        public double GetPrev(string paramName, int year)
        {
            if (isYearActual(year)) return Get(paramName, year - 1);
            else return Get(paramName, _cy);
        }
        public double GetNext(string paramName, int year)
        {
            if (isYearActual(year)) return Get(paramName, year + 1);
            else return Get(paramName, _cy);
        }

        protected void doCalc(string paramName, string[] argParams, SimpleCalc func)
        {
            doCalc(paramName, argParams, func, ProjectPreriod.Period);
        }
        protected void doCalc(string paramName, string[] argParams, SimpleCalc func, ProjectPreriod usePeriod)
        {
            if (workSpace == null) return;//!!!
            double[] args = new double[argParams.Length];
            bool _vector = false;
            foreach (string s in argParams)
                if (IsVector(s)) { _vector = true; break; }
            if (_vector)
            {
                int yearFrom, yearTo;
                workSpace.GetPeriodEx(usePeriod, out yearFrom, out yearTo);
                for (int y = yearFrom; y <= yearTo; y++)
                {
                    for (int i = 0; i < argParams.Length; i++)
                        args[i] = Get(argParams[i], y);
                    Add(paramName, y, func(args));
                }
            }
            else
            {
                for (int i = 0; i < argParams.Length; i++)
                    args[i] = Get(argParams[i], _cy);
                Add(paramName, func(args));
            }
        }
        public static ParamIdent ParamIdent(string dataName,string paramName)
        {
            ParamIdent res;
            res.DataName = dataName;
            res.ParamName = paramName;
            return res;
        }

        protected void doCalc(string paramName, ParamIdent[] argParams, SimpleCalc func)
        {
            doCalc(paramName, argParams, func, ProjectPreriod.Period);
        }
        protected void doCalc(string paramName, ParamIdent[] argParams, SimpleCalc func, ProjectPreriod usePeriod)
        {
            if (workSpace == null) return;//!!!
            double[] args = new double[argParams.Length];
            BaseData bd;
            bool _vector = false;
            foreach (ParamIdent i in argParams)
            {
                if (string.IsNullOrEmpty(i.DataName) || i.DataName == name)
                {
                    if (IsVector(i.ParamName)) { _vector = true; break; }
                }
                else
                {
                    if (workSpace.DataExists(i.DataName, out bd) && bd.IsVector(i.ParamName))
                    { _vector = true; break; }
                }
            }

            if (_vector)
            {
                int yearFrom, yearTo;
                workSpace.GetPeriodEx(usePeriod,out yearFrom, out yearTo);
                for (int y = yearFrom; y <= yearTo; y++)
                {
                    for (int i = 0; i < argParams.Length; i++)
                    {
                        if (string.IsNullOrEmpty(argParams[i].DataName) || 
                            argParams[i].DataName == name)
                            args[i] = Get(argParams[i].ParamName, y);
                        else
                        {
                            if (workSpace.DataExists(argParams[i].DataName, out bd))
                                args[i] = bd.Get(argParams[i].ParamName, y);
                            else args[i]=0;
                        }
                    }
                    Add(paramName, y, func(args));
                }
            }
            else
            {
                for (int i = 0; i < argParams.Length; i++)
                {
                    if (string.IsNullOrEmpty(argParams[i].DataName) ||
                        argParams[i].DataName == name)
                        args[i] = Get(argParams[i].ParamName, _cy);
                    else
                    {
                        if (workSpace.DataExists(argParams[i].DataName, out bd))
                            args[i] = bd.Get(argParams[i].ParamName, _cy);
                        else args[i] = 0;
                    }
                }
                Add(paramName, func(args));
            }
        }

        /// <summary>
        /// сумма значений по параметру
        /// </summary>
        /// <param name="paramName">параметр</param>
        /// <param name="yearFor">год, до которго считать (включительно)</param>
        /// <returns></returns>
        public double Sum(string paramName, int yearFor)
        {
            double res = 0;
            Dictionary<string, double> v;
            if (data.TryGetValue(_cy, out v) && v.TryGetValue(paramName.ToLower(),out res)) {}
            else
            {
                foreach (int y in data.Keys)
                {
                    double d;
                    if (isYearActual(y) && y<=yearFor && data[y].TryGetValue(paramName.ToLower(), out d))
                        res += d;
                }
            }
            return res;
        }

        private const string _groupDelimiter = "|"; // разделитель группа|параметр

        public bool GroupsPresented(List<string> groups)
        {
            groups.Clear();
            foreach (KeyValuePair<string, bool> k in _params)
            {
                if (k.Key.Contains(_groupDelimiter))
                {
                    int i = k.Key.IndexOf(_groupDelimiter);
                    string s = k.Key.Substring(0, i);
                    if (groups.IndexOf(s) == -1) groups.Add(s);
                }
            }
            return groups.Count > 0;
        }

        public bool paramGroupPresented(string groupName, Dictionary<string, bool> paramList)
        {
            paramList.Clear();
            foreach (KeyValuePair<string, bool> k in _params)
            {
                if (k.Key.ToLower().StartsWith(groupName.ToLower() + _groupDelimiter))
                    paramList.Add(k.Key, k.Value);
            }
            return paramList.Count > 0;
        }

        public bool paramGroupPresented(string groupName, List<string> paramList)
        {
            paramList.Clear();
            foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
            {
                foreach (string s in k.Value.Keys)
                {
                    if (s.StartsWith(groupName.ToLower() + _groupDelimiter) && paramList.IndexOf(s) == -1)
                        paramList.Add(s);
                }
            }
            return paramList.Count > 0;
        }

        public bool IsVector(string paramName)
        {
            return !_cyPresented(paramName);
        }
        public bool IsEmpty(string paramName)
        {
            bool res=true;
            foreach (int y in data.Keys)
            {
                if (data[y].ContainsKey(paramName.ToLower()))
                {
                    res = false;
                    break;
                }
            }
            return res;
        }

        /// <summary>
        /// Вычисляет год, значение при котором максимально
        /// </summary>
        /// <param name="paramName"></param>
        /// <returns></returns>
        public int SupremumYear(string paramName)
        {
            double value;
            return SupremumYear(paramName, out value);
        }

        public int SupremumYear(string paramName, out double value)
        {
            int res = 0; value = 0;
            double v = double.MinValue;
            foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
            {
                foreach (KeyValuePair<string, double> j in k.Value)
                {
                    if (j.Key.ToLower() == paramName.ToLower())
                    {
                        if (j.Value > v)
                        {
                            res = k.Key;
                            v = j.Value;
                            value = v;
                        }
                        break;
                    }
                }
            }
            return res;
        }


        private int _firstYear()
        {
            if (workSpace == null) return 0;//!!!
            int yearFrom,yearTo;
            workSpace.GetPeriod(out yearFrom,out yearTo);
            return yearFrom;
        }

        /// <summary>
        /// возвращает значения всех имеющихся параметров на указанную дату
        /// </summary>
        /// <param name="yearFor"></param>
        /// <param name="paramValues"></param>
        public void GetValuesForYear(int yearFor, Dictionary<string, double> paramValues)
        {
            paramValues.Clear();
            Dictionary<string, double> v;
            if (data.TryGetValue(_cy, out v))
            {
                foreach (KeyValuePair<string, double> k in v) paramValues.Add(k.Key, k.Value);
            }
            if (isYearActual(yearFor) && data.TryGetValue(yearFor, out v))
            {
                foreach (KeyValuePair<string, double> k in v)
                    if (paramValues.ContainsKey(k.Key)) paramValues[k.Key] = k.Value;
                    else paramValues.Add(k.Key, k.Value);
            }
        }

        /// <summary>
        /// значения параметра по годам (для векторов)
        /// </summary>
        /// <param name="paramName"></param>
        /// <param name="output"></param>
        public void GetAllValuesOfParam(string paramName, out Dictionary<int, double> output)
        {
            output = new Dictionary<int, double>();
            foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
            {
                double v;
                if (isYearActual(k.Key) && _get1(paramName, k.Key, out v))
                    output.Add(k.Key, v);
            }
        }
        public void GetAllValuesOfParam(string dataName, string paramName, out Dictionary<int, double> output)
        {
            output = new Dictionary<int, double>();
            if (workSpace == null)
            {
                if (string.Compare(name, dataName, true) == 0) GetAllValuesOfParam(paramName, out output);
            }
            else
                foreach (BaseData d in workSpace.Data)
                {
                    if (d.name.ToLower() == dataName.ToLower())
                    {
                        d.GetAllValuesOfParam(paramName, out output);
                        break;
                    }
                }
        }
        public double GetTotalForParam(string paramName)
        {
            double res = 0;
            if (IsVector(paramName))
            {
                Dictionary<int, double> yv;
                GetAllValuesOfParam(paramName, out yv);
                foreach (KeyValuePair<int, double> k in yv)
                    res += k.Value;
            }
            else
            {
                int y0, y1; 
                workSpace.GetPeriod(out y0, out y1);
                res = Get(paramName, 0) * (double)(y1 - y0);
            }
            return res;
        }


        #region Visualize
        string templateBook,templateSheet;

        /// <summary>
        /// Имя файла шаблона: Excel-книга
        /// </summary>
        public string TemplateBook
        {
            get { return templateBook; }
            set { templateBook = value; }
        }
        /// <summary>
        /// Название листа в шаблоне
        /// </summary>
        public string TemplateSheet
        {
            get { return templateSheet; }
            set { templateSheet = value; }
        }
        public bool SetTemplate(string Book, string Sheet)
        {
            TemplateBook = Book;
            TemplateSheet = Sheet;
            return true;
        }

        private bool CheckTemplate(bool alert,out string err)
        {
            err = "";
            if (string.IsNullOrEmpty(templateBook)) err = "Не указан файл шаблона";
            else
            {
                if (!File.Exists(templateBook)) err = "Не найден файл шаблона: " + templateBook;
                else
                {
                    if (string.IsNullOrEmpty(templateSheet)) err = "Не указан лист в шаблоне";
                    else
                    {
                        List<string> sheetNames = new List<string>();
                        WorkSheetUtils.GetSheetNames(templateBook, sheetNames, out err);
                        if (err == "")
                        {
                            bool sheetFounded = false;
                            foreach (string s in sheetNames)
                            {
                                if (s.ToLower() == templateSheet.ToLower())
                                    {
                                        sheetFounded = true;
                                        break;
                                    }
                            }
                            if (!sheetFounded) err = "Не найден лист в шаблоне: " + templateSheet;
                        }
                    }
                }
            }

            bool res = err == "";
            if (alert && !res)
                MessageBox.Show(err, "Ошибка в шаблоне", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            return res;
        }


        /// <summary>
        /// делает Visualize, игнорируя workSpace.ActiveWorkBook
        /// </summary>
        public void Visualize1(string tempBook, string tempSheet)
        {
            if (!File.Exists(tempBook))
            {
                MessageBox.Show("Файл шаблона не найден" + Environment.NewLine + tempBook, "", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            ApplicationClass a = new ApplicationClass();
            a.DisplayAlerts = false;
            a.SheetsInNewWorkbook = 1;
            Workbook wb = a.Workbooks.Add(Missing.Value);

            string err;
            //теперь копируем лист из книги template
            if (WorkSheetUtils.CopyTemplate(wb, tempBook, tempSheet, out err))
            {
                /// w - вот тот лист, в котором находится структура, куда "размазываются" данные
                Worksheet w = (Worksheet)wb.Worksheets[wb.Worksheets.Count];
                string _sname = WorkSheetUtils.SetSheetName(WorkSpace.Preffix + name);
                w.Name = _sname;
                w.Visible = XlSheetVisibility.xlSheetHidden;
                doVisualize(w);
                w.Visible = XlSheetVisibility.xlSheetVisible;
                //w.Activate();
                ((Worksheet)(wb.Worksheets[1])).Delete();
                a.Visible = true;
            }
            else
                MessageBox.Show(err, "", MessageBoxButtons.OK, MessageBoxIcon.Information);

        }

        /// <summary>
        /// визуализация данных
        /// </summary>
        /// <param name="template">лист Excel - шаблон</param>
        /// <param name="destination">лист Excel, в котором визуализирются данные</param>
        public void Visualize()
        {
            if (workSpace == null) return;//!!!
            string err;
            if (workSpace.ActiveWorkBook == null) return;
            bool da = workSpace.ActiveWorkBook.Application.DisplayAlerts;
            workSpace.ActiveWorkBook.Application.DisplayAlerts = false;
            //название листа
            string _sname = WorkSheetUtils.SetSheetName(WorkSpace.Preffix + name);
            //из текущей книги удаляем (затем вызывается метод worksheet.copy - это
            //делается для сохронания форматов ячеек)
            int j=-1;
            for (int i=1;i<=workSpace.ActiveWorkBook.Worksheets.Count;i++)
                if (((Worksheet)(workSpace.ActiveWorkBook.Worksheets[i])).Name.ToLower() == _sname.ToLower())
                {
                    j = i;
                    break;
                }
            //в книге не может быть 0 листов...
            bool _one = workSpace.ActiveWorkBook.Worksheets.Count == 1;
            if (j != -1) 
            {
                if (_one)
                {
                    workSpace.ActiveWorkBook.Worksheets.Add(
                        workSpace.ActiveWorkBook.Worksheets[1], Type.Missing, 1, XlSheetType.xlWorksheet);
                    j++;
                }
                ((Worksheet)(workSpace.ActiveWorkBook.Worksheets[j])).Delete();
            }
            //теперь копируем лист из книги template
            Directory.SetCurrentDirectory(workSpace.BaseDir);
            if (File.Exists(templateBook) &&
                WorkSheetUtils.CopyTemplate(workSpace.ActiveWorkBook, Path.GetFullPath(templateBook), templateSheet, out err))
            {
                /// w - вот тот лист, в котором находится структура, куда "размазываются" данные
                Worksheet w = (Worksheet)workSpace.ActiveWorkBook.Worksheets[workSpace.ActiveWorkBook.Worksheets.Count];
                w.Name = _sname;
                w.Visible = XlSheetVisibility.xlSheetHidden;
                doVisualize(w);
                w.Visible = XlSheetVisibility.xlSheetVisible;
                //w.Activate();
            }
            if (_one && workSpace.ActiveWorkBook.Worksheets.Count>0)
                ((Worksheet)(workSpace.ActiveWorkBook.Worksheets[1])).Delete();
            workSpace.ActiveWorkBook.Application.DisplayAlerts = da;
        }

        public void doVisualize(Worksheet w)
        {
            if (workSpace == null) return;//!!!
            //Header
            int r0, r1;
            List<TemplateXlsItem> L = new List<TemplateXlsItem>();
            if (TemplateXlsMisc.getHeaders(w, L,out r0,out r1))
            {
                foreach (TemplateXlsItem ti in L)
                {
                    if (IsEmpty(ti.paramName)) w.get_Range(ti.cell, ti.cell).set_Value(Missing.Value, null);
                    else
                    {
                        double v;
                        if (IsVector(ti.paramName))
                        {
                            if (_get1(ti.paramName, _firstYear(), out v)) w.get_Range(ti.cell, ti.cell).Value2 = v;
                            else w.get_Range(ti.cell, ti.cell).set_Value(Missing.Value, null);
                        }
                        else
                        {
                            if (_get1(ti.paramName, _cy, out v)) w.get_Range(ti.cell, ti.cell).Value2 = v;
                            else w.get_Range(ti.cell, ti.cell).set_Value(Missing.Value, null);
                        }
                    }
                }
            }
            if (r0 != 0)
            {
                w.get_Range(WorkSheetUtils.RC(1, r0), WorkSheetUtils.RC(1, r0)).EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);
                r1--;
            }
            if (r1 != 0)
                w.get_Range(WorkSheetUtils.RC(1, r1), WorkSheetUtils.RC(1, r1)).EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);

            //data
            //предполагается, что между <Data> и </Data> одна строка
            //более одной на данный момент излишне(не актуально)...
            if (TemplateXlsMisc.getDatas(w,L,out r0,out r1,r1))
            {
                int yearFrom,yearTo;
                int cTo = w.UsedRange.Columns.Count;
                workSpace.GetPeriod(out yearFrom, out yearTo);
                if (this is AmortizationData)
                    yearFrom -= workSpace.MaxAmortizationTerm();
                int[,] years = new int[yearTo - yearFrom + 1,1];
                for (int i = 0; i <= yearTo - yearFrom; i++)
                {
                    if (i < (yearTo - yearFrom))
                    {
                        w.get_Range(WorkSheetUtils.RC(1, r0 + 2), WorkSheetUtils.RC(1, r0 + 2)).EntireRow.Insert(
                            XlInsertShiftDirection.xlShiftDown, Type.Missing);
                        r1++;
                    }
                    years[i, 0] = yearFrom + i;
                }
                w.get_Range(WorkSheetUtils.RC(1, r0 + 1), WorkSheetUtils.RC(cTo, r0 + 1)).AutoFill(
                    w.get_Range(WorkSheetUtils.RC(1, r0 + 1), WorkSheetUtils.RC(cTo, r1)),
                    XlAutoFillType.xlFillCopy);
                foreach (TemplateXlsItem ti in L)
                {
                    int _c1 = w.get_Range(ti.cell, ti.cell).Column;
                    int _r1 = w.get_Range(ti.cell, ti.cell).Row + (yearTo - yearFrom);
                    if (ti.paramName.ToLower() == "год")
                        w.get_Range(ti.cell, WorkSheetUtils.RC(_c1, _r1)).set_Value(Missing.Value, years);
                    else
                    {
                        if (IsEmpty(ti.paramName))
                            w.get_Range(ti.cell, WorkSheetUtils.RC(_c1, _r1)).set_Value(Missing.Value, null);
                        else
                        {
                            double[,] values = new double[yearTo - yearFrom + 1, 1];
                            if (IsVector(ti.paramName)) //так будет бестрее (скаляры)
                            {
                                for (int i = 0; i <= yearTo - yearFrom; i++)
                                    values[i, 0] = Get(ti.paramName, years[i, 0]);
                            }
                            else
                            {
                                double v = Get(ti.paramName, _cy);
                                for (int i = 0; i <= yearTo - yearFrom; i++)
                                    values[i, 0] = v;
                            }
                            w.get_Range(ti.cell, WorkSheetUtils.RC(_c1, _r1)).set_Value(Missing.Value, values);
                        }
                    }
                }
            }
            if (r0 != 0)
            {
                w.get_Range(WorkSheetUtils.RC(1, r0), WorkSheetUtils.RC(1, r0)).EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);
                r1--;
            }
            if (r1 != 0)
                w.get_Range(WorkSheetUtils.RC(1, r1), WorkSheetUtils.RC(1, r1)).EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);


            //footer
            if (TemplateXlsMisc.getFooters(w, L,out r0,out r1,r1))
            {
                foreach (TemplateXlsItem ti in L)
                {
                    if (IsEmpty(ti.paramName)) w.get_Range(ti.cell, ti.cell).set_Value(Missing.Value, null);
                    else
                    {
                        double v;
                        if (IsVector(ti.paramName))
                        {
                            if (_get1(ti.paramName, _firstYear(), out v)) w.get_Range(ti.cell, ti.cell).Value2 = v;
                            else w.get_Range(ti.cell, ti.cell).set_Value(Missing.Value, null);
                        }
                        else
                        {
                            if (_get1(ti.paramName, _cy, out v)) w.get_Range(ti.cell, ti.cell).Value2 = v;
                            else w.get_Range(ti.cell, ti.cell).set_Value(Missing.Value, null);
                        }
                    }
                }
            }
            if (r0 != 0)
            {
                w.get_Range(WorkSheetUtils.RC(1, r0), WorkSheetUtils.RC(1, r0)).EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);
                r1--;
            }
            if (r1 != 0)
                w.get_Range(WorkSheetUtils.RC(1, r1), WorkSheetUtils.RC(1, r1)).EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);
        }
        #endregion

        /// <summary>
        /// Список названий параметров имеющих место быть
        /// </summary>
        /// <param name="list"></param>
        public bool GetAvailableParamNames(List<string> list)
        {
            list.Clear();
            list.AddRange(_params.Keys);
            return list.Count > 0;
        }

        public bool GetAvailableParamNames(List<string> list, bool loadable)
        {
            list.Clear();
            foreach (KeyValuePair<string, bool> k in _params)
                if (k.Value == loadable) list.Add(k.Key);
            return list.Count > 0;
        }

        /// <summary>
        /// Список загружаемых параметров 
        /// (используется для настройки загрузчиков)
        /// </summary>
        /// <param name="list"></param>
        public bool GetLoadableParamNames(List<string> list)
        {
            list.Clear();
            foreach (KeyValuePair<string, bool> k in _params)
                if (k.Value) list.Add(k.Key);
            return list.Count > 0;
        }
        public bool IsLoadableParamPresented()
        {
            bool res = false;
            foreach(bool b in _params.Values)
                if (b)
                {
                    res = true;
                    break;
                }
            return res;
        }

        /// Xml: SaveTo, LoadFrom

        public void SaveToXml(XmlNode node, bool saveData)
        {
            XmlElement e = node.OwnerDocument.CreateElement("BaseData");
            XmlElement en = node.OwnerDocument.CreateElement("Name");
            en.InnerText = name; e.AppendChild(en);
            XmlElement es = node.OwnerDocument.CreateElement("ShortName");
            es.InnerText = shortName; e.AppendChild(es);
            XmlElement etb = node.OwnerDocument.CreateElement("TemplateBook");
            etb.InnerText = templateBook; e.AppendChild(etb);
            XmlElement ets = node.OwnerDocument.CreateElement("TemplateSheet");
            ets.InnerText = templateSheet; e.AppendChild(ets);

            XmlElement ei = node.OwnerDocument.CreateElement("Params");
            foreach (KeyValuePair<string, bool> k in _params)
            {
                XmlElement _ei = node.OwnerDocument.CreateElement("Param");
                XmlElement _ep = node.OwnerDocument.CreateElement("ParamName");
                _ep.InnerText = k.Key; _ei.AppendChild(_ep);
                XmlElement _el = node.OwnerDocument.CreateElement("Loadable");
                _el.InnerText = k.Value.ToString(); _ei.AppendChild(_el);
                ei.AppendChild(_ei);
            }
            e.AppendChild(ei);

            if (saveData)
            {
                XmlElement di = node.OwnerDocument.CreateElement("Datas");
                foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
                {
                    XmlElement _di = node.OwnerDocument.CreateElement("Data");
                    XmlElement _dy = node.OwnerDocument.CreateElement("Year");
                    _dy.InnerText = k.Key.ToString(); _di.AppendChild(_dy);
                    foreach (KeyValuePair<string, double> j in k.Value)
                    {
                        if (j.Value != 0.0)
                        {
                            XmlElement _ditem = node.OwnerDocument.CreateElement("Item");
                            XmlElement _dp = node.OwnerDocument.CreateElement("ParamName");
                            _dp.InnerText = j.Key; _ditem.AppendChild(_dp);
                            XmlElement _dv = node.OwnerDocument.CreateElement("ParamValue");
                            _dv.InnerText = j.Value.ToString(); _ditem.AppendChild(_dv);
                            _di.AppendChild(_ditem);
                        }
                    }
                    di.AppendChild(_di);
                }
                e.AppendChild(di);
            }
            node.AppendChild(e);
        }

        public void LoadFromXml(XmlNode node, bool loadData)
        {
            data.Clear();
            defaultParams();
            XmlNode nn = node.SelectSingleNode("Name");
            if (nn != null) name = nn.InnerText;
            XmlNode ns = node.SelectSingleNode("ShortName");
            if (ns != null) ShortName = ns.InnerText;
            XmlNode ntb = node.SelectSingleNode("TemplateBook");
            if (ntb != null) templateBook = ntb.InnerText;
            XmlNode nts = node.SelectSingleNode("TemplateSheet");
            if (nts != null) templateSheet = nts.InnerText;

            XmlNode ni = node.SelectSingleNode("Params");
            if (ni != null)
            {
                foreach (XmlNode _ni in ni.ChildNodes)
                    if (_ni.Name == "Param")
                    {
                        XmlNode _np = _ni.SelectSingleNode("ParamName");
                        if (_np != null && _np.InnerText.Trim() != "")
                        {
                            bool _l = false;
                            XmlNode _nl = _ni.SelectSingleNode("Loadable");
                            if (_nl != null) bool.TryParse(_nl.InnerText, out _l);
                            AddParam(_np.InnerText, _l);
                        }
                    }
            }
            if (loadData)
            {
                XmlNode di = node.SelectSingleNode("Datas");
                if (di != null)
                {
                    foreach (XmlNode _di in di.ChildNodes)
                    {
                        if (_di.Name == "Data")
                        {
                            XmlNode _dy = _di.SelectSingleNode("Year");
                            int y;
                            if (_dy != null && int.TryParse(_dy.InnerText, out y))
                            {
                                Dictionary<string,double> d = new Dictionary<string,double>();
                                foreach (XmlNode _ditem in _di.ChildNodes)
                                {
                                    if (_ditem.Name == "Item")
                                    {
                                        XmlNode _dp = _ditem.SelectSingleNode("ParamName");
                                        if (_dp != null)
                                        {
                                            double v;
                                            XmlNode _dv = _ditem.SelectSingleNode("ParamValue");
                                            if (_dv != null && Misc.misc._TryToDouble(_dv.InnerText, out v))
                                                d.Add(_dp.InnerText, v);
                                        }
                                    }
                                }
                                data.Add(y, d);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// clone without data
        /// </summary>
        /// <returns></returns>
        public BaseData CreateAs(WorkSpace ws,bool copyData)
        {
            BaseData res;

            //Reflection!
            ConstructorInfo ci = GetType().GetConstructor(new Type[] { typeof(WorkSpace) });
            if (ci != null)
                res = (BaseData)(ci.Invoke(new object[] { ws }));
            else
            {
                //какой-то объект от базового класса непосредственно
                //для него конструктор BaseData(WorkSpace WorkSpace,string Name)
                ci = GetType().GetConstructor(new Type[] { typeof(WorkSpace),typeof(string) });
                res = (BaseData)(ci.Invoke(new object[] { ws,name }));
            }

            res.shortName = shortName;
            res._params.Clear();
            foreach (KeyValuePair<string, bool> k in _params)
                res._params.Add(k.Key, k.Value);
            res.templateBook = templateBook;
            res.templateSheet = templateSheet;
            if (copyData)
            {
                foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
                {
                    Dictionary<string, double> v = new Dictionary<string, double>();
                    foreach (KeyValuePair<string, double> j in k.Value)
                        v.Add(j.Key, j.Value);
                    res.data.Add(k.Key, v);
                }
            }
            return res;
        }

        public void AddData(BaseData DataTo)
        {
            foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
            {
                Dictionary<string, double> vTo;
                bool f = DataTo.data.TryGetValue(k.Key, out vTo);
                if (!f) vTo = new Dictionary<string,double>();

                double _v;
                foreach (KeyValuePair<string, double> j in k.Value)
                {
                    if (vTo.TryGetValue(j.Key, out _v))
                    {
                        _v += j.Value;
                        vTo[j.Key] = _v;
                    }
                    else
                        vTo.Add(j.Key, j.Value);
                    if (!DataTo.ParamExist(j.Key))
                        DataTo.AddParam(j.Key, false);
                }

                if (f) DataTo.data[k.Key]=vTo;
                else DataTo.data.Add(k.Key,vTo);
            }
        }

        public virtual void SetProfitValues(int profit)
        {
            doSetYearValues(profit, "Рентабельный срок");
        }

        public virtual void SetEndProjectValues(int endProject)
        {
            doSetYearValues(endProject, "Проектный срок");
        }

        private static string[] preffixes = {"Рентабельный срок","Проектный срок"};
        private static bool isPreffixed(string paramName)
        {
            bool res=false;
            foreach(string s in preffixes)
            {
                if (paramName.ToLower().StartsWith(s.ToLower() + "_"))
                {
                    res = true;
                    break;
                }
            }
            return res;
        }

        private void doSetYearValues(int year, string paramPreffix)
        {
            Dictionary<string, double> v0;
            bool f0 = true;
            if (!data.TryGetValue(0,out v0)) {f0=false; v0= new Dictionary<string,double>();}

            if (v0.ContainsKey(paramPreffix.ToLower() + "_год")) v0[paramPreffix.ToLower() + "_год"] = (double)year;
            else v0.Add(paramPreffix.ToLower() + "_год", (double)year);

            Dictionary<string, double> paramValues = new Dictionary<string,double>();
            GetValuesForYear(year, paramValues);
            foreach (KeyValuePair<string, double> k in paramValues)
            {
                bool f = true;
                double _value = k.Value;
                if (!k.Key.ToLower().EndsWith("год") && paramPreffix == "Проектный срок")
                    f = getTotalValueForEndPeriod(k.Key, out _value);

                if (f)
                    if (isPreffixed(k.Key))
                    {
                        if (k.Key.ToLower().StartsWith(paramPreffix.ToLower() + "_"))
                        {
                            if (v0.ContainsKey(k.Key.ToLower())) v0[k.Key.ToLower()] = _value;
                            else v0.Add(k.Key.ToLower(), _value);
                        }
                    }
                    else
                    {
                        string s = paramPreffix.ToLower() + "_" + k.Key.ToLower();
                        if (v0.ContainsKey(s)) v0[s] = _value;
                        else v0.Add(s, _value);
                    }
            }
            if (f0) data[0] = v0;
            else data.Add(0, v0);
        }

        #region for eProject

        public void CreateDataTableStructure(System.Data.DataTable dt, bool fill)
        {
            dt.Rows.Clear(); dt.Columns.Clear();

            DataColumn cy = new DataColumn("Год", typeof(int));
            cy.ReadOnly = !(WorkSpace.isInputData(name));
            dt.Columns.Add(cy);
            if (!WorkSpace.isInputData(this))
                dt.Columns.Add(new DataColumn("tag",typeof(string)));
            foreach (KeyValuePair<string, bool> k in _params)
            {
                DataColumn c = new DataColumn(k.Key, typeof(double));
                c.ReadOnly = !k.Value;
                dt.Columns.Add(c);
            }
            if (fill)
                FillDataTable(dt);
        }
        public void CreateDataTableStructure(System.Data.DataTable dt)
        {
            CreateDataTableStructure(dt, false);
        }

        /// <summary>
        /// привязан ли конкретный загрузчик
        /// </summary>
        public bool IsOneLoaderPresented(ref ILoader ldr)
        {
            ldr = null;
            if (workSpace == null) return false;//!!!
            foreach(ILoader l in workSpace.loaders)
                if (l.DataNameTo == name)
                {
                    ldr = l;
                    break;
                }
            return ldr != null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dt"></param>
        public void FillDataTable(System.Data.DataTable dt)
        {
            if (WorkSpace.isInputData(name))
            {
                //для вх.данных заполняются только те данные, которые определены 
                //в defaultParams()    

                BaseData d;

                //Reflection!
                ConstructorInfo ci = GetType().GetConstructor(new Type[] { typeof(WorkSpace) });
                if (ci != null)
                    d = (BaseData)(ci.Invoke(new object[] { workSpace }));
                else
                {
                    //какой-то объект от базового класса непосредственно
                    //для него конструктор BaseData(WorkSpace WorkSpace,string Name)
                    ci = GetType().GetConstructor(new Type[] { typeof(WorkSpace), typeof(string) });
                    d = (BaseData)(ci.Invoke(new object[] { workSpace, name }));
                }
                if (GetType() == typeof(StandartsData))
                {
                    foreach (DataRow r in dt.Rows)
                        if (Convert.ToInt32(r["cTag"]) == 0 && d._params.ContainsKey(r["cParam"].ToString()))
                            r["cValue"] = Get(r["cParam"].ToString(), 0);
                }
                else //прочие вх.
                {
                    dt.Rows.Clear();

                    foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
                    {
                        if (isYearActual(k.Key))
                        {
                            DataRow r = dt.NewRow();
                            r["Год"] = k.Key;
                            foreach (KeyValuePair<string, double> j in k.Value)
                                if (d.ParamExist(j.Key))
                                    r[j.Key] = j.Value;
                            dt.Rows.Add(r);
                        }
                    }

                }
            }
            else //вых.
            {
                dt.Rows.Clear();
                bool f0 = false;
                foreach (KeyValuePair<int, Dictionary<string, double>> k in data)
                {
                    if (k.Key == 0) f0 = true;
                    else
                    {
                        DataRow r = dt.NewRow();
                        r["Год"] = k.Key;
                        foreach (KeyValuePair<string, double> j in k.Value)
                            r[j.Key] = j.Value;
                        dt.Rows.Add(r);
                    }
                }
                if (f0)
                {
                    Dictionary<string, double> v0;
                    data.TryGetValue(0, out v0);
                    //добавить, что с преффиксами
                    foreach (string sp in preffixes)
                    {
                        if (Get(sp.ToLower() + "_год", 0) != 0)
                        {
                            DataRow r = dt.NewRow();
                            if (string.Compare(WorkSpace.ProfitSTR,sp,true)==0)
                                r["tag"] =  workSpace.Unprofitable ? WorkSpace.UnprofitSTR:WorkSpace.ProfitSTR;
                            else r["tag"] = sp;
                            
                            dt.Rows.Add(r);
                            DataRow r1 = dt.NewRow();
                            //порядок полей ...
                            foreach (DataColumn c in dt.Columns)
                                if (c.ColumnName != "tag")
                                {
                                    double v;
                                    string s = sp.ToLower() + "_" + c.ColumnName.ToLower();
                                    if (v0.TryGetValue(s, out v))
                                        r1[c.ColumnName] = v;
                                }
                            r1["tag"] = sp;
                            dt.Rows.Add(r1);
                        }
                    }
                    //сроки окупаемости
                    DataRow r2 = dt.NewRow();
                    int pb = workSpace.PaybackYear();
                    switch (pb)
                    {
                        case -1: r2["tag"] = "Срок окупаемости отсутствует"; break;
                        case 0: r2["tag"] = "Срок окупаемости менее года"; break;
                        default: r2["tag"] = "Срок окупаемости (лет): "+pb.ToString(); break;
                    }
                    dt.Rows.Add(r2);
                }
            }

        }

        /// <summary>
        /// чистка "незагружаемых параметров" - prepareCalc
        /// </summary>
        public void ClearNonLoadableParams()
        {
            foreach(KeyValuePair<string,bool> k in _params)
                if (!k.Value)
                {
                    foreach (KeyValuePair<int, Dictionary<string, double>> j in data)
                        if (j.Value.ContainsKey(k.Key.ToLower())) j.Value.Remove(k.Key.ToLower());
                }
        }

        public void UnionWith(BaseData d)
        {
            d.AddData(this);
        }

        /// <summary>
        /// Использется для заполнения итоговой строки по проектному сроку
        /// (часть параметров суммируются, часть "как есть" и т.д.)
        /// </summary>
        /// <param name="paramName"></param>
        /// <param name="totalValue"></param>
        /// <returns></returns>
        public virtual bool getTotalValueForEndPeriod(string paramName,out double totalValue)
        {
            totalValue = 0;
            return false;
        }


        public string _showParams()
        {
            string res = name + Environment.NewLine;
            foreach(KeyValuePair<string, bool>k in _params)
                res +=k.Key+Environment.NewLine;
            return res;
        }

        #endregion

        #region Com

        [ComRegisterFunctionAttribute]
        public static void RegisterFunction(Type type)
        {
            Registry.ClassesRoot.CreateSubKey(
              GetSubKeyName(type, "Programmable"));

            RegistryKey key = Registry.ClassesRoot.OpenSubKey(
              GetSubKeyName(type, "InprocServer32"), true);

            key.SetValue("",
              System.Environment.SystemDirectory + @"\mscoree.dll",
              RegistryValueKind.String);
        }

        [ComUnregisterFunctionAttribute]
        public static void UnregisterFunction(Type type)
        {
            Registry.ClassesRoot.DeleteSubKey(
              GetSubKeyName(type, "Programmable"), false);
        }

        private static string GetSubKeyName(Type type,
          string subKeyName)
        {
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            s.Append(@"CLSID\{");
            s.Append(type.GUID.ToString().ToUpper());
            s.Append(@"}\");
            s.Append(subKeyName);
            return s.ToString();
        }

        #endregion

    }
    public struct ParamIdent
    {
        //если DataName=="", то текущие данные
        public string DataName, ParamName;
    }

    //виды периода
    public enum ProjectPreriod { Period, PredPeriod, Full }

}
